//
//  KSAdPlayerView.m
//  KSAdPlayer
//
//  Created by 徐志军 on 2019/10/10.
//  Copyright © 2019 KuaiShou. All rights reserved.
//

#import "KSAdPlayerView.h"
#import "KSAdPlayerView+Internal.h"
#import "KSAdBasePlayerView+Internal.h"
#import "BlocksKit.h"


typedef NS_ENUM(NSInteger, KSAdPlayerErrorStatus) {
    KSAdPlayerErrorStatus_CreateAVPlayerItem            =           1000,
    KSAdPlayerErrorStatus_AVPlayerStatusFailed          =           1001,
};


static NSString * const KSAdVideoPlayerErrorDomain = @"KSAdVideoPlayerErrorDomain";

static NSString *ksAdPlayerItemStatus = @"status";
static NSString *ksAdPlayerItemPlaybackLikelyToKeepUp = @"playbackLikelyToKeepUp";
static NSString *ksAdPlayerItemPlaybackBufferEmpty = @"playbackBufferEmpty";
static NSString *ksAdPlayerItemLoadedTimeRanges = @"loadedTimeRanges";

@interface KSAdPlayerView()<UIGestureRecognizerDelegate>


@property (nonatomic, strong) KSAdInfo *adInfo;

//播放属性
@property (nonatomic, strong) AVPlayer *queuePlayer;
//@property (nonatomic, strong) AVQueuePlayer *queuePlayer;
@property (nonatomic, strong, readwrite) AVPlayerItem *playerItem;
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
@property (nonatomic, strong) id timeObserverToken;


// 当前播放器是否有效
@property (nonatomic, assign) BOOL isSystemInterruptPause;
@property (nonatomic, assign) NSInteger resumeTryCount;
@property (nonatomic, assign) NSInteger maxResumeTryCount;
@property (nonatomic, assign) NSInteger lastReportSecond;
@property (nonatomic, assign) CGSize videoSize;
@property (nonatomic, assign, readwrite) BOOL m_isPlaying;
@property (nonatomic, assign, readwrite) BOOL m_isFinish;

@property (nonatomic, readonly) BOOL isValid;

@property (nonatomic, assign) BOOL notificationAddStatus;
@property (nonatomic, assign) BOOL systemNotificationAddStatus;

@property (nonatomic, assign ,readwrite) BOOL playerErrorStatus;

@end


@implementation KSAdPlayerView
#pragma mark - Constructor And Destructor
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // 有黑空白,上下有黑空白,或者左右有黑空白
        self.videoGravity = AVLayerVideoGravityResizeAspect;
        
        self.observerTimeInterval = 1.0f;
        self.enableVideoCache = NO;
        
        [self _resetPlayerControl];
    }
    return self;
}

- (void)dealloc {
    NSLog(@"%@ dealloc", NSStringFromClass([self class]));
    [self _releasePlayerResourceAndResetPlayerControl];
}

#pragma mark - Action
- (void)viewAction:(id)sender {

}

- (void)seekWithTime:(CGFloat)time {
    // Do Nothing
}

- (void)updateVideoGravity:(AVLayerVideoGravity)videoGravity {
    if (![self.videoGravity isEqualToString:videoGravity]) {
        self.videoGravity = videoGravity;
        self.playerLayer.videoGravity = self.videoGravity;
    }
}

- (void)autoVideoGravity {
    CGSize videoSize = self.videoSize;
    AVLayerVideoGravity dest = AVLayerVideoGravityResizeAspect;
    if (videoSize.width < videoSize.height) { // 竖版视频使用填充模式
        dest = AVLayerVideoGravityResizeAspectFill;
    }
    
    if (![self.videoGravity isEqualToString:dest]) {
        self.videoGravity = dest;
        self.playerLayer.videoGravity = self.videoGravity;
    }
    
//    // 此处为默认视频填充模式
//    self.videoGravity = AVLayerVideoGravityResize;
//    // 充满屏幕播放视频,但是部分被裁剪了
//    self.videoGravity = AVLayerVideoGravityResizeAspectFill;
//    // 有黑空白,上下有黑空白,或者左右有黑空白
//    self.videoGravity = AVLayerVideoGravityResizeAspect;
}

#pragma mark - Override
- (void)layoutSubviews {
    [super layoutSubviews];
    self.playerLayer.frame = self.bounds;
}

#pragma mark - Notification
- (void)playerItemDidPlayToEndTimeNotification:(NSNotification *)notification {
    // xzj_todo need to do
//    if (!self.m_isPlaying) {
//        return;
//    }
    self.m_isPlaying = NO;
    self.m_isFinish = YES;
    NSLog(@"playerViewDidReachEnd %p adInfo:%@", self, self.adInfo);
    [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
        if ([delegate respondsToSelector:@selector(playerViewDidReachEnd:)]) {
            [delegate playerViewDidReachEnd:self];
        }
    }];

    [self _removeNotificationAndObserver];
}

// 耳机插入、拔出事件
- (void)audioSessionRouteChangeNotification:(NSNotification*)notification {
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            // 耳机插入
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
        {
            UIApplicationState state = [UIApplication sharedApplication].applicationState;
            if (state != UIApplicationStateActive) {
                return;
            }
            // 拔掉耳机继续播放
            if (self.m_isPlaying) {
                [self.queuePlayer play];
            }
        }
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            break;
    }
}
- (void)applicationDidEnterBackgroundNotification:(id)sender {
    NSLog(@"applicationDidEnterBackgroundNotification");
    [self _pauseBySystemInterrupt];
}

- (void)applicationWillResignActiveNotification:(id)sender {
    NSLog(@"applicationWillResignActiveNotification");
    [self _pauseBySystemInterrupt];
}

- (void)applicationDidBecomeActiveNotification:(id)sender {
    NSLog(@"applicationDidBecomeActiveNotification");
    [self _resumeBySystemInterrupt];
}
- (void)applicationWillEnterForegroundNotification:(id)sender {
    NSLog(@"applicationWillEnterForegroundNotification");
    [self _resumeBySystemInterrupt];
}

- (void)audioSessionInterruptionNotification:(id)sender {
    NSNotification *notification = sender;
    NSDictionary *userInfo = notification.userInfo;
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        NSLog(@"audioSessionInterruptionNotification UIApplicationStateActive : %@", [userInfo description]);
        [self _resumeBySystemInterrupt];
    } else {
        NSLog(@"audioSessionInterruptionNotification UIApplicationStateActive Not : %@", [userInfo description]);
        [self _pauseBySystemInterrupt];
    }
}

#pragma mark - Private(Pause&Resume SystemInterrupt)
- (void)_pauseBySystemInterrupt {
    if (self.isValid && self.m_isPlaying && !self.m_isFinish) {
        NSLog(@"systemInterrupt: pause");
        self.isSystemInterruptPause = YES;
        [self pause]; // pause 会移走Notification，所以要把Notification给添加进来
        [self _addNotification];
        if (self.pauseBySystemInterruptBlock) {
            self.pauseBySystemInterruptBlock();
        }
    }
}

- (void)_resumeBySystemInterrupt {
    if (self.isSystemInterruptPause) {
        NSLog(@"systemInterrupt: resume");
        self.isSystemInterruptPause = NO;
        // play会添加Notification，所以先移走Notification
        [self _removeNotification];
        [self play];
        if (self.resumeBySystemInterruptBlock) {
            self.resumeBySystemInterruptBlock();
        }
    }
}
#pragma mark - Private(Release)
- (void)_releasePlayerResourceAndResetPlayerControl {
    [self _removeNotificationAndObserver];
    if (self.playerLayer) {
        [self.playerLayer removeFromSuperlayer];
        self.playerLayer = nil;
    }
    if (self.playerItem) {
        self.playerItem = nil;
    }
    if (self.queuePlayer) {
        [self.queuePlayer pause];
        [self.queuePlayer replaceCurrentItemWithPlayerItem:nil];
        self.queuePlayer = nil;
    }
    [self _resetPlayerControl];
}

- (void)_resetPlayerControl {
    self.isSystemInterruptPause = NO;
    self.resumeTryCount = 0;
    self.maxResumeTryCount = 3;
    self.m_isPlaying = NO;
    self.m_isFinish = NO;
    self.lastReportSecond = 0;
    self.videoSize = CGSizeMake(0, 0);
    self.notificationAddStatus = NO;
    self.systemNotificationAddStatus = NO;
}
- (CGFloat)_calcLoadedDuration {
    CGFloat loadedDuration = 0.0f;
    if (self.queuePlayer && self.queuePlayer.currentItem) {
        NSArray *loadedTimeRanges = self.queuePlayer.currentItem.loadedTimeRanges;
        if (loadedTimeRanges && [loadedTimeRanges count]) {
            CMTimeRange timeRange = [[loadedTimeRanges firstObject] CMTimeRangeValue];
            CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
            CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
            loadedDuration = startSeconds + durationSeconds;
        }
    }
    return loadedDuration;
}

- (NSString *)_playerStatusDescription {
    NSString *result = [NSString stringWithFormat:@"播放器当前状态, m_isPlaying:%d m_isFinish:%d", self.m_isPlaying, self.m_isFinish];
    return result;
}

#pragma mark - Manage Notification
- (void)_addNotificationAndObserver {
    if (!self.notificationAddStatus) {
        self.notificationAddStatus = YES;
        [self _addPlayerItemObserver];
        [self _addTimeObserverToken];
        [self _addNotification];
    }
}
- (void)_removeNotificationAndObserver {
    if (self.notificationAddStatus) {
        self.notificationAddStatus = NO;
        [self _removePlayerItemObserver];
        [self _removeTimeObserverToken];
        [self _removeNotification];
    }
}
- (void)_addPlayerItemObserver {
    if (!self.playerItem) {
        return;
    }
    KSAdPlayerView * __weak weak_self = self;
    [self.playerItem bk_addObserverForKeyPath:ksAdPlayerItemStatus identifier:ksAdPlayerItemStatus options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew task:^(id  _Nonnull obj, NSDictionary * _Nonnull change) {
        [weak_self observeValueForKeyPath:ksAdPlayerItemStatus ofObject:weak_self.playerItem change:change context:NULL];
    }];
    
    [self.playerItem bk_addObserverForKeyPath:ksAdPlayerItemPlaybackLikelyToKeepUp identifier:ksAdPlayerItemPlaybackLikelyToKeepUp options:NSKeyValueObservingOptionNew task:^(id  _Nonnull obj, NSDictionary * _Nonnull change) {
        [weak_self observeValueForKeyPath:ksAdPlayerItemPlaybackLikelyToKeepUp ofObject:weak_self.playerItem change:change context:NULL];
    }];
    
    
    [self.playerItem bk_addObserverForKeyPath:ksAdPlayerItemPlaybackBufferEmpty identifier:ksAdPlayerItemPlaybackBufferEmpty options:NSKeyValueObservingOptionNew task:^(id  _Nonnull obj, NSDictionary * _Nonnull change) {
        [weak_self observeValueForKeyPath:ksAdPlayerItemPlaybackBufferEmpty ofObject:weak_self.playerItem change:change context:NULL];
    }];
    
    [self.playerItem bk_addObserverForKeyPath:ksAdPlayerItemLoadedTimeRanges identifier:ksAdPlayerItemLoadedTimeRanges options:NSKeyValueObservingOptionNew task:^(id  _Nonnull obj, NSDictionary * _Nonnull change) {
        [weak_self observeValueForKeyPath:ksAdPlayerItemLoadedTimeRanges ofObject:weak_self.playerItem change:change context:NULL];
    }];
}
- (void)_removePlayerItemObserver {
    if (!self.playerItem) {
        return;
    }
    // 移除playerItem的KVO
    [self.playerItem cancelPendingSeeks];
    [self.playerItem bk_removeAllBlockObservers];
//    [self.playerItem removeObserver:self forKeyPath:@"status" context:nil];
//    [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
//    [self.playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty" context:nil];
//    [self.playerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp" context:nil];
}
- (void)_addNotification {
    if (self.systemNotificationAddStatus) {
        return;
    }
    self.systemNotificationAddStatus = YES;
    // 播放完毕的通知
//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidPlayToEndTimeNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    // 耳机插入和拔掉通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionRouteChangeNotification:) name:AVAudioSessionRouteChangeNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionInterruptionNotification:) name:AVAudioSessionInterruptionNotification object:nil];
}
- (void)_removeNotification {
    self.systemNotificationAddStatus = NO;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)_addTimeObserverToken {
    // 添加播放时间观察
    if (self.timeObserverToken == nil && self.queuePlayer) {
        __weak typeof (self) weakSelf = self;
        self.timeObserverToken = [self.queuePlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            
            
            CGFloat progress = CMTimeGetSeconds(weakSelf.queuePlayer.currentItem.currentTime) / CMTimeGetSeconds(weakSelf.queuePlayer.currentItem.duration);
            if (progress >= 1.0f) {
                // 使用这种方式来监听播放的结束通知
                NSLog(@"time observe play end");
                [weakSelf playerItemDidPlayToEndTimeNotification:nil];
            }
            __strong typeof(weakSelf) strongSelf = weakSelf;
            CGFloat second = CMTimeGetSeconds(time);
            [strongSelf callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                if ([delegate respondsToSelector:@selector(playerView:currentTime:)]) {
                    [delegate playerView:strongSelf currentTime:second];
                }
            }];
        }];
    }
}
- (void)_removeTimeObserverToken {
    // 移除时间观察
    [self.queuePlayer removeTimeObserver:self.timeObserverToken];
    self.timeObserverToken = nil;
}

#pragma mark - ReadOnly
- (AVURLAsset *)urlAsset {
    return (AVURLAsset *)self.playerItem.asset;
}
#pragma mark - Public

- (void)replay {
    CGFloat currentTime = self.currentTime;
    if (currentTime == 0) {
        NSLog(@"abc-re:replay play");
        self.lastReportSecond = 0;
        [self play];
    } else {
        NSLog(@"abc-re:replay resume");
        [self resumeWithTime:0];
    }
}

- (void)prepareWithURL:(NSURL *)URL {
    [self setURL:URL];
}

- (void)setURL:(NSURL *)URL {
    if ([self.urlAsset.URL.absoluteString isEqualToString:URL.absoluteString]) {
        return;
    }
    AVURLAsset *urlAsset = [AVURLAsset assetWithURL:URL];
    [self changeCurrentURLAsset:urlAsset];
}

- (void)changeCurrentURLAsset:(AVURLAsset *)urlAsset {
    // 如果有正在播放的视频 先释放掉
    [self _releasePlayerResourceAndResetPlayerControl];
    // 初始化playerItem
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:urlAsset];
    if (playerItem == nil) {
        NSError *error = [NSError errorWithDomain:KSAdVideoPlayerErrorDomain code:KSAdPlayerErrorStatus_CreateAVPlayerItem userInfo:@{NSLocalizedDescriptionKey : @"Unable to create AVPlayerItem."}];
        NSLog(@"playerView:didFailWithError: error:%@", error);
        [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
            if ([delegate respondsToSelector:@selector(playerView:didFailWithError:)]) {
                [delegate playerView:self didFailWithError:error];
            }
        }];
        return;
    }
    self.playerItem = playerItem;
    if (@available(iOS 10.0, *)) {
        self.playerItem.preferredForwardBufferDuration = 10.f;
    } else {
        // Fallback on earlier versions
    }
    self.queuePlayer = [AVPlayer playerWithPlayerItem:self.playerItem];
    if (!self.videoSoundEnable) {
        self.queuePlayer.volume = 0;
    }
    if (@available(iOS 10.0, *)) {
        self.queuePlayer.automaticallyWaitsToMinimizeStalling = YES;
    }
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.queuePlayer];
    self.playerLayer.videoGravity = self.videoGravity;
        // xzj_todo 播放器问题1
    //    https://www.jianshu.com/p/244c2420246e
    
    [self _resetVideoSize];
    // 添加playerLayer到self.layer
    [self.layer insertSublayer:self.playerLayer atIndex:0];
    [self setNeedsLayout];
    [self layoutIfNeeded];
}

// xzj_todo 80多秒的时候，滑动列表会卡顿， 30秒的时候不会卡顿
- (void)resumeWithTime:(CGFloat)time {
    self.playerErrorStatus = NO;
    self.resumeTryCount = 0;
    self.lastReportSecond = 0;
    NSLog(@"seekToTime KSAdPlayerView resume time:%.2f", time);
    self.queuePlayer.rate = 1.0f;
    [self.queuePlayer.currentItem cancelPendingSeeks];
    if (fabs(self.currentTime - time) <= 0.01) {
        //间隙较小时直接播放
        [self.queuePlayer play];
    } else {
        __weak typeof(self) weakSelf = self;
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [weakSelf _resumeWithTime:time];
        }];
    }
}

- (void)_resumeWithTime:(CGFloat)time {
    CMTime cmTime = CMTimeMakeWithSeconds(time, 1);
    __weak typeof(self) weakSelf = self;
    dispatch_block_t seek = ^{
        if (!CMTIME_IS_INVALID(cmTime)) {
            if (weakSelf.queuePlayer.currentItem.status == AVPlayerStatusReadyToPlay) {
                [weakSelf.queuePlayer seekToTime:cmTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {

                    weakSelf.m_isPlaying = YES;
                    weakSelf.m_isFinish = NO;
                    if (finished) {
                        NSLog(@"seekToTime finish");
//                        [weakSelf.queuePlayer play];
                    } else {
                        NSLog(@"seekToTime finish not");
                    }
                    // seek成功后，要播放一下
                    [weakSelf play];
                }];
            } else {
                NSLog(@"seekToTime status not ready");
                [weakSelf.queuePlayer play];
                if (weakSelf.resumeTryCount < 3) {
                    weakSelf.resumeTryCount += 1;
                    NSLog(@"seekToTime try %zd times", weakSelf.resumeTryCount);
                    [weakSelf _resumeWithTime:time];
                } else {
                    NSLog(@"seekToTime try 3 times and failed to resume");
                    // resume失败后，一定要尝试播放
                    [weakSelf play];
                }
            }
        }
    };
    
    if (self.queuePlayer.currentItem.status == AVPlayerStatusReadyToPlay) {
        NSLog(@"seekToTime status ready");
        seek();
    } else {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            seek();
        });
    }
}
- (void)openBlackAreaClick {
    
}

// 播放
- (void)play {
    // 只有不处于被系统事件中断的时候才真正的播放
    // 系统中断和系统恢复是一个成对的行为
    [self setPlayBehaviorAndType];
    self.playerErrorStatus = NO;
    if (self.isSystemInterruptPause) {
        return;
    } else {
        [self _addNotificationAndObserver];
        self.m_isPlaying = YES;
        self.queuePlayer.rate = 1.0f;
        [self.queuePlayer play];
    }
}
//设置播放类型和播放行为
- (void)setPlayBehaviorAndType {
}

// 暂停
- (void)pause {
    if (self.m_isPlaying) {
        [self _removeNotificationAndObserver];
        self.m_isPlaying = NO;
    }
    [self.queuePlayer pause];
}

// 停止播放
- (void)stop {
    [self _releasePlayerResourceAndResetPlayerControl];
}

- (void)changeVolume:(CGFloat)volume {
    self.queuePlayer.volume = volume;
}

- (void)captureCurrentFrameWithCallback:(void (^)(UIImage * _Nullable))callback {
    if (!self.urlAsset) {
        if (callback) {
            callback(nil);
        }
        return;
    }
    AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.urlAsset];
    [generator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:self.playerItem.currentTime]] completionHandler:^(CMTime requestedTime, CGImageRef  _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
        UIImage *currentFrame = nil;
        if (result == AVAssetImageGeneratorSucceeded) {
            currentFrame = [UIImage imageWithCGImage:image];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if (callback) {
                callback(currentFrame);
            }
        });
    }];
}

#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // player状态
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus newStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
        AVPlayerStatus oldStatus = [[change objectForKey:NSKeyValueChangeOldKey] integerValue];
        if (newStatus != oldStatus) {
            switch (newStatus) {
                case AVPlayerItemStatusUnknown:
                    NSLog(@"AVPlayerItemStatusUnknown");
                    break;
                case AVPlayerItemStatusReadyToPlay: {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                            if ([delegate respondsToSelector:@selector(playerViewIsReadyToPlayVideo:)]) {
                                [delegate playerViewIsReadyToPlayVideo:self];
                            }
                        }];
                    });
                }
                    break;
                case AVPlayerStatusFailed: {
                    self.m_isFinish = YES;
                    NSError *error = [NSError errorWithDomain:KSAdVideoPlayerErrorDomain code:KSAdPlayerErrorStatus_AVPlayerStatusFailed userInfo:@{NSLocalizedDescriptionKey : @"unknown player error, status == AVPlayerItemStatusFailed"}];
                    NSLog(@"AVPlayerStatusFailed playerView:didFailWithError: error:%@", error);
                    // 在iOS10中弱网情况下，极大概率会出现failed，如果这个时候不stop的话，那么就会导致崩溃，AVPlayerItem已经释放了，但是继续给AVPlayerItem发送KVO导致崩溃
                    [self stop];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                            if ([delegate respondsToSelector:@selector(playerView:didFailWithError:)]) {
                                self.playerErrorStatus = YES;
                                [delegate playerView:self didFailWithError:error];
                            }
                        }];
                    });
                }
                    break;
            }
        }
    } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
        NSLog(@"playbackBufferEmpty  %d  %@", (self.playerItem.playbackBufferEmpty), [self _playerStatusDescription]);
        // 缓冲为空
        if (self.queuePlayer.currentItem.playbackBufferEmpty) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                    if ([delegate respondsToSelector:@selector(playerViewPlaybackBufferEmpty:)]) {
                        [delegate playerViewPlaybackBufferEmpty:self];
                    }
                }];
            });
        }
    } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
        NSLog(@"playbackLikelyToKeepUp %d  %@", (self.playerItem.playbackLikelyToKeepUp), [self _playerStatusDescription]);
        if (self.queuePlayer.currentItem.playbackLikelyToKeepUp) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                    if ([delegate respondsToSelector:@selector(playerViewPlaybackLikelyToKeepUp:)]) {
                        [delegate playerViewPlaybackLikelyToKeepUp:self];
                    }
                }];
            });
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        CGFloat loadedDuration = [self _calcLoadedDuration];
        NSLog(@"loadedDuration:%.4f  %@", loadedDuration, [self _playerStatusDescription]);
        dispatch_async(dispatch_get_main_queue(), ^{
            [self callDelegates:^(id<KSAdBasePlayerViewDelegate> delegate) {
                if ([delegate respondsToSelector:@selector(playerView:loadedTimeRangeDidChange:)]) {
                    [delegate playerView:self loadedTimeRangeDidChange:loadedDuration];
                }
            }];
        });
    } else {
        NSLog(@"%@   %@", keyPath, [self _playerStatusDescription]);
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark - Readonly
- (CGFloat)duration {
    if (self.queuePlayer) {
        return CMTimeGetSeconds(self.queuePlayer.currentItem.duration);
    }
    return 0.0f;
}

- (CGFloat)currentTime {
    if (self.queuePlayer) {
        return CMTimeGetSeconds([self.queuePlayer currentTime]);
    }
    return 0.0f;
}

- (BOOL)isValid {
    return self.urlAsset != nil;
}

- (void)_resetVideoSize {
    AVURLAsset *urlAsset = self.urlAsset;
    NSArray *keys = @[@"tracks"];
    __weak typeof(urlAsset) weakAsset = urlAsset;
    __block CGSize videoSize = CGSizeZero;
    KSAdPlayerView * __weak weak_self = self;
    [urlAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
        NSArray *array = weakAsset.tracks;
        for (AVAssetTrack *track in array) {
            if ([track.mediaType isEqualToString:AVMediaTypeVideo]) {
                videoSize = track.naturalSize;
            }
        }
        weak_self.videoSize = videoSize;
    }];
}

- (BOOL)isFinish {
    return self.m_isFinish;
}
- (BOOL)isPlaying {
    return self.m_isPlaying;
}
- (BOOL)canPlayOrPause {
    return YES;
}

@end

